from PIL import Image
import cv2
import numpy as np
import argparse
from pathlib import Path
import win32api
import win32gui
import win32ui
import win32con
import yaml
from bdtime import Time
from tools import get_public_attrs
from tools import kk, ms
from bdtime import tt
from tools.enum_window import get_hwnd_by_enum, get_title_bar_size, move_wind_to_left_top, bring_window_to_top


gvars = argparse.Namespace()


CONFIG_PATH = Path(__file__).parent.parent.joinpath("config.yaml")
assert CONFIG_PATH.is_file()

with open(CONFIG_PATH, encoding='utf-8') as f:
    result = yaml.safe_load(f)
    DEFAULT_MONITOR_WIDTH = result.get("windows").get("monitor_width")
    DEFAULT_MONITOR_HEIGHT = result.get("windows").get("monitor_height")
    WINDOW_NAME = result.get("game").get("window_name")


def cap(region=None, fmt='RGB'):
    return cap_raw(gvars.genshin_window_rect_img if region is None else (
        region[0] + gvars.genshin_window_rect_img[0], region[1] + gvars.genshin_window_rect_img[1], region[2],
        region[3]),
                   fmt=fmt)


def cap_raw(region=None, fmt='RGB'):
    if region is not None:
        left, top, w, h = region
        # w = x2 - left + 1
        # h = y2 - top + 1
    else:
        w = DEFAULT_MONITOR_WIDTH  # set this
        h = DEFAULT_MONITOR_HEIGHT  # set this
        left = 0
        top = 0

    hwnd = win32gui.FindWindow(None, WINDOW_NAME)
    # hwnd = win32gui.GetDesktopWindow()
    wDC = win32gui.GetWindowDC(hwnd)
    dcObj = win32ui.CreateDCFromHandle(wDC)
    cDC = dcObj.CreateCompatibleDC()
    dataBitMap = win32ui.CreateBitmap()

    dataBitMap.CreateCompatibleBitmap(dcObj, w, h)

    cDC.SelectObject(dataBitMap)
    cDC.BitBlt((0, 0), (w, h), dcObj, (left, top), win32con.SRCCOPY)
    # dataBitMap.SaveBitmapFile(cDC, bmpfilenamename)
    signedIntsArray = dataBitMap.GetBitmapBits(True)
    # img = np.fromstring(signedIntsArray, dtype="uint8")
    img = np.frombuffer(signedIntsArray, dtype="uint8")

    img.shape = (h, w, 4)

    # Free Resources
    dcObj.DeleteDC()
    cDC.DeleteDC()
    win32gui.ReleaseDC(hwnd, wDC)
    win32gui.DeleteObject(dataBitMap.GetHandle())

    if fmt == 'BGR':
        return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGBA2BGR)
    elif fmt == 'RGB':
        return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGBA2RGB)
    else:
        raise ValueError('Cannot indetify this fmt')


def match_img(img, target, type=cv2.TM_CCOEFF_NORMED, threshold=0.8):
    h, w = target.shape[:2]
    res = cv2.matchTemplate(img, target, type)

    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    res = cv2.matchTemplate(img, target, cv2.TM_CCOEFF_NORMED)

    # 使用 np.where 获取匹配结果中大于阈值的位置
    loc = np.where(res >= threshold)
    # 如果不存在匹配点，说明没找到目标
    if loc[0].size == 0:
        return None

    if type in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        return (
            *min_loc,
            min_loc[0] + w,
            min_loc[1] + h,
            min_loc[0] + w // 2,
            min_loc[1] + h // 2,
        )
    else:
        return (
            *max_loc,
            max_loc[0] + w,
            max_loc[1] + h,
            max_loc[0] + w // 2,
            max_loc[1] + h // 2,
        )


def psnr(img1, img2):
    mse = np.mean((img1 / 255.0 - img2 / 255.0) ** 2)
    if mse < 1.0e-10:
        return 100
    PIXEL_MAX = 1
    return 20 * np.log10(PIXEL_MAX / np.sqrt(mse))


def distance(x1, y1, x2, y2):
    return np.sqrt(np.square(x1 - x2) + np.square(y1 - y2))


def show_img(img_arr, title="img", resize=(1280, 720)):
    if resize:
        img_arr = cv2.resize(img_arr, resize)
    cv2.imshow('img_arr', img_arr)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


class ImageNames:
    activities = 'activities'
    test_self_skill = 'test_self_skill'
    start_test = 'start_test'
    close_by_click_anywhere = 'close_by_click_anywhere'
    exit = 'exit'
    confirm_exit = 'confirm_exit'


img_names = get_public_attrs(ImageNames)
img_dc = dict(zip(img_names, [cv2.imread(f'../images/{img_name}.png') for img_name in img_names]))


def click_img_center(img_name: str, threshold=0.8, first_wait_time=0.5, max_wait_time=3, need_press_alt=False):
    if need_press_alt:
        tt.sleep(0.5)
        kk.press('alt')

    tt.sleep(first_wait_time)

    img = img_dc.get(img_name)
    assert img is not None, 'img不能为空!'

    t1 = Time()

    pos = None
    while not pos and t1.during(max_wait_time):
        pos = match_img(cap_raw(), img, threshold=threshold)
        tt.sleep(0.5)
        tt.stop_alt('x')

    assert pos, f"未找到img[{img_name}]"
    ms.click_left(*pos[-2:])

    if need_press_alt:
        tt.sleep(0.5)
        kk.release('alt')


class AutoEnterAndExit:
    def __init__(self):
        from tools import set_genshin_window_position
        set_genshin_window_position()
        ms.click_middle()

    def enter_test_self(self):
        click_img_center(ImageNames.activities, first_wait_time=1, need_press_alt=True, max_wait_time=5)
        click_img_center(ImageNames.test_self_skill)
        click_img_center(ImageNames.start_test)

    def exit_test_self(self):
        click_img_center(ImageNames.exit, need_press_alt=True)
        click_img_center(ImageNames.confirm_exit)


if __name__ == '__main__':
    hwnd = get_hwnd_by_enum()
    move_wind_to_left_top(hwnd, client_top_left=False)
    # move_wind_to_left_top(hwnd, client_top_left=True)
    window_rect, client_rect, title_bar_width, title_bar_height = get_title_bar_size(hwnd)
    bring_window_to_top(hwnd)

    for i in range(5):
        click_img_center(ImageNames.activities, first_wait_time=1 if i == 0 else 3, need_press_alt=True, max_wait_time=5)
        click_img_center(ImageNames.test_self_skill)
        click_img_center(ImageNames.start_test)
        click_img_center(ImageNames.close_by_click_anywhere, max_wait_time=8)
        click_img_center(ImageNames.exit, need_press_alt=True)
        click_img_center(ImageNames.confirm_exit)

        tt.sleep(15)


